package mailx

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"net/smtp"
	"os"
)

type ServerOption struct {
	Name string `json:"name,omitempty"`
	From string `json:"from,omitempty"`
	User string `json:"user,omitempty"`
	Pass string `json:"pass,omitempty"`
	Host string `json:"host,omitempty"`
	Port string `json:"port,omitempty"`
}

func (server ServerOption) Valid() error {
	if server.Host == "" {
		return fmt.Errorf("服务器地址(SMTP HOST)未设置")
	}
	if server.Port == "" {
		return fmt.Errorf("服务器端口(SMTP PORT)未设置")
	}
	if server.From == "" {
		return fmt.Errorf("发件地址(From Email Address)未设置")
	}
	return nil
}

func (server ServerOption) String() string {
	return fmt.Sprintf("smtp://%s:%s@%s:%s?name=%s&from=%s", server.User, server.Pass, server.Host, server.Port, server.Name, server.From)
}

func Send(ctx context.Context, rcpts []string, body Body, serverOpts ServerOption) (err error) {
	if err = serverOpts.Valid(); err != nil {
		err = fmt.Errorf("smtp server valid: %w", err)
		return
	}

	var (
		d net.Dialer
		n net.Conn
		c *smtp.Client
		w io.WriteCloser
	)

	// net.DialTimeout()
	if n, err = d.DialContext(ctx, "tcp", net.JoinHostPort(serverOpts.Host, serverOpts.Port)); err != nil {
		err = fmt.Errorf("smtp dial smtp: %w", err)
		return
	}

	if c, err = smtp.NewClient(n, serverOpts.Host); err != nil {
		err = fmt.Errorf("smtp create client: %w", err)
		return
	}

	defer c.Close()

	local, _ := os.Hostname()
	if local == "" {
		local = "localhost"
	}

	if err = c.Hello(local); err != nil {
		err = fmt.Errorf("smtp hello: %w", err)
		return
	}

	if ok, _ := c.Extension("STARTTLS"); ok {
		config := &tls.Config{ServerName: serverOpts.Host}
		if err = c.StartTLS(config); err != nil {
			err = fmt.Errorf("smtp starttls: %w", err)
			return
		}
	}

	if serverOpts.User != "" {
		if ok, _ := c.Extension("AUTH"); !ok {
			err = fmt.Errorf("smtp: server doesn't support AUTH")
			return
		}

		if err = c.Auth(smtp.PlainAuth("", serverOpts.User, serverOpts.Pass, serverOpts.Host)); err != nil {
			err = fmt.Errorf("smtp auth: %w", err)
			return
		}
	}

	if err = c.Mail(serverOpts.From); err != nil {
		err = fmt.Errorf("smtp mail: %w", err)
		return
	}

	for _, addr := range rcpts {
		if err = c.Rcpt(addr); err != nil {
			err = fmt.Errorf("smtp rcpt: %w", err)
			return
		}
	}

	if w, err = c.Data(); err != nil {
		err = fmt.Errorf("smtp data: %w", err)
		return
	}

	if err = body(w); err != nil {
		err = fmt.Errorf("smtp write body: %w", err)
		_ = w.Close()
		return
	}

	if err = w.Close(); err != nil {
		err = fmt.Errorf("smtp close body: %w", err)
		return
	}

	if err = c.Quit(); err != nil {
		err = fmt.Errorf("smtp quit: %w", err)
		return
	}
	return
}
